package abel.wlmr.model.unit;

import java.util.ArrayList;
import java.util.List;

import abel.wlmr.model.WlmrBaseUnit;
import abel.wlmr.model.WlmrException;
import abel.wlmr.model.type.C1DateTimeType;
import abel.wlmr.model.type.DataIdBody;
import abel.wlmr.model.type.PeriodReportField;
import io.netty.buffer.ByteBuf;

/**
 * B.5.4.1.3.18 F65：定时上报1类数据任务设置
 * pn 表示 1～64 个任务序号，数据单元格式见表 B.32。
 * 
 * 表B.32 定时上报 1 类数据任务设置数据单元格式
 * 数据内容
 * 数据格式 字节数 说明
 * D7 D6 D5 D4 D3 D2 D1 D0
 * 定时上报周期单位 定时上报周期 BIN 1
 * 上报基准时间：秒分时日月年 见附录 C.1 6
 * 曲线数据抽取倍率 R BIN 1
 * 数据单元标识个数 n BIN 1
 * 数据单元标识 1
 * 见本附录
 * B.5.12.1.2
 * 4 在请求 1 类数
 * 据报文规定的
 * 数据单元标识
 * 中选取。
 * ……
 * 数据单元标识 n 4
 * ——定时上报周期单位：
 * 用 D6～D7 编码表示，取值 0～3 依次表示分、时、日、月。
 * ——定时上报周期：
 * 用 D0～D5 表示，为定时上报数据的时间周期。
 * ——上报基准时间：
 * 终端以此设定的时间开始上报，并按定时上报周期循环。
 * ——曲线抽取数据倍率 R：取值范围 1～96，
 * 表示终端按此倍率抽取数据上送，如被抽取的数据的冻结密度 m=2，即每 30 分钟冻结一个值，那
 * 么当 R=2 时，表示按 60 分钟抽取， R=1 时，表示仍按 30 分钟抽取。
 * ——数据单元标识个数 n：
 * 表示本任务具有的数据单元标识个数
 * 
 * @author Abel
 *
 */
public class Afn04F6566Unit extends WlmrBaseUnit {
    private static final short MIN_LEN = 1 + 6 + 1 + 1;

    private PeriodReportField rptPeriod = new PeriodReportField();

    private C1DateTimeType startReportTime;

    private byte r;

    private short taskCnt;

    private List<DataIdBody> taskList = new ArrayList<>();

    public Afn04F6566Unit() {
        super();
    }

    public Afn04F6566Unit(short guessedLen) {
        super(guessedLen);
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.AppUnitPart#appendBuf(io.netty.buffer.ByteBuf)
     */
    @Override
    public ByteBuf appendBuf(ByteBuf buf) {
        if (rptPeriod == null) {
            throw new IllegalArgumentException("rpt period should not be null");
        }
        if (startReportTime == null || startReportTime.getDtValue() == null) {
            throw new IllegalArgumentException("start report time should not be null");
        }

        if (taskCnt < 1) {
            throw new IllegalArgumentException("term count should be positive");
        }
        if (taskList == null || taskCnt != taskList.size()) {
            throw new IllegalArgumentException("term list should be same size as task count");
        }

        buf.writeByte(rptPeriod.getValue());
        startReportTime.appendBuf(buf);
        buf.writeByte(r);
        buf.writeByte(taskCnt);
        for (int i = 0; i < taskCnt; i++) {
            DataIdBody uid = taskList.get(i);
            if (uid == null) {
                throw new IllegalArgumentException("none of term list can be null");
            }

            uid.appendBuf(buf);
        }

        return buf;
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.AppUnitPart#parseBuf(io.netty.buffer.ByteBuf)
     */
    @Override
    public int parseBuf(ByteBuf buf) throws WlmrException {
        if (getGuessedLength() < MIN_LEN) {
            throw new WlmrException("not enough bytes for 04-F6566");
        }

        rptPeriod.parseValue(buf.readByte());

        startReportTime = new C1DateTimeType(C1DateTimeType.FIXED_LEN);
        startReportTime.parseBuf(buf);

        r = buf.readByte();
        if (r < 1 || r > 96) {
            throw new WlmrException("invalid r in UP packet");
        }

        taskCnt = (short) (buf.readByte() & 0xFF);
        if (getGuessedLength() < MIN_LEN + 4 * taskCnt) {
            throw new WlmrException("not enough bytes for 04-F6566");
        }

        taskList = new ArrayList<>();
        for (int i = 0; i < taskCnt; i++) {
            DataIdBody uid = new DataIdBody();
            uid.parseBuf(buf);

            taskList.add(uid);
        }

        return MIN_LEN + 4 * taskCnt;
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.WlmrDataPart#getMinLength()
     */
    @Override
    public int getMinLength() {
        return MIN_LEN;
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.WlmrDataPart#getFinalLen()
     */
    @Override
    public short getFinalLen() {
        if (finalLen < 0) {
            finalLen = MIN_LEN;
            for (DataIdBody t : taskList) {
                finalLen += t.getFinalLen();
            }
        }
        return finalLen;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return String.format("{rptPeriod:%s, startReportTime:%s, r:%s, taskCnt:%s, taskList:%s}", rptPeriod,
            startReportTime, r, taskCnt, taskList);
    }

    /**
     * @return the rptPeriod
     */
    public PeriodReportField getRptPeriod() {
        return rptPeriod;
    }

    /**
     * @param rptPeriod
     *            the rptPeriod to set
     */
    public void setRptPeriod(PeriodReportField rptPeriod) {
        this.rptPeriod = rptPeriod;
    }

    /**
     * @return the startReportTime
     */
    public C1DateTimeType getStartReportTime() {
        return startReportTime;
    }

    /**
     * @param startReportTime
     *            the startReportTime to set
     */
    public void setStartReportTime(C1DateTimeType startReportTime) {
        if (startReportTime == null || startReportTime.getDtValue() == null) {
            throw new IllegalArgumentException("start report time should be SMHDMY");
        }

        this.startReportTime = startReportTime;
    }

    /**
     * @return the r
     */
    public byte getR() {
        return r;
    }

    /**
     * @param r
     *            the r to set
     */
    public void setR(byte r) {
        if (r < 1 || r > 96) {
            throw new IllegalArgumentException("r should be [1,96]");
        }
        this.r = r;
    }

    /**
     * @return the taskCnt
     */
    public short getTaskCnt() {
        return taskCnt;
    }

    /**
     * @param taskCnt
     *            the taskCnt to set
     */
    public void setTaskCnt(short taskCnt) {
        this.taskCnt = taskCnt;
    }

    /**
     * @return the taskList
     */
    public List<DataIdBody> getTaskList() {
        return taskList;
    }

    /**
     * @param taskList
     *            the taskList to set
     */
    public void setTaskList(List<DataIdBody> taskList) {
        this.taskList = taskList;
    }

}
